package cn.iocoder.yudao.module.ai.service.material;

import cn.hutool.core.date.DateUtil;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.exception.ServerException;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;
import cn.iocoder.yudao.framework.exception.BusinessException;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.ai.constant.DicConstant;
import cn.iocoder.yudao.module.ai.controller.admin.material.vo.MaterialListVO;
import cn.iocoder.yudao.module.ai.controller.admin.materialreleasefile.vo.MaterialReleaseFileRespVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.materialreleasefile.MaterialReleaseFileDO;
import cn.iocoder.yudao.module.ai.dal.mysql.materialreleasefile.MaterialReleaseFileMapper;
import cn.iocoder.yudao.module.ai.service.materialreleasefile.MaterialReleaseFileService;
import cn.iocoder.yudao.module.ai.utils.DataSetUtil;
import cn.iocoder.yudao.module.ai.utils.UnZipUtils;
import cn.iocoder.yudao.module.ai.utils.vo.MaterialFileVo;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.validation.constraints.NotEmpty;

import org.springframework.validation.annotation.Validated;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

import cn.iocoder.yudao.module.ai.controller.admin.material.vo.*;
import cn.iocoder.yudao.module.ai.dal.dataobject.material.MaterialDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;

import cn.iocoder.yudao.module.ai.dal.mysql.material.MaterialMapper;
import org.springframework.web.multipart.MultipartFile;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;


/**
 * 素材管理 Service 实现类
 *
 * @author 侯俊青
 */
@Slf4j
@Service
@Validated
public class MaterialServiceImpl implements MaterialService {

    @Resource
    private DataSetUtil dataSetUtil;
    /**
     * 应用名
     */
    @Value("${material.root-path}")
    private String materialRootPath;
    /**
     * zip 文件夹名称
     */
    @Value("${material.source-folder}")
    private String sourceFolderName;
    /**
     * zip解压文件文件夹名称
     */
    @Value("${material.release-folder}")
    private String releaseFolderName;

    /**
     * 公共-上传到本地文件的路径
     */
    @Value("${yudao.file.upload-path}")
    private String commonUploadPath;

    @Resource
    private MaterialMapper materialMapper;
    @Resource
    private MaterialReleaseFileMapper materialReleaseFileMapper;

    @Resource
    private MaterialReleaseFileService materialReleaseFileService;

    @Resource
    private AdminUserApi adminUserApi ;
    @Resource
    private DeptApi deptApi ;

    @Override
    public Long saveMaterial(MaterialSaveReqVO saveReqVO) {

        // 设置基本信息
        MaterialDO material = BeanUtils.toBean(saveReqVO, MaterialDO.class);
        material.setFileUploadPath(null);
        material.setContainFileNum(0);
        material.setSuplierId(deptApi.queryFirstDept(SecurityFrameworkUtils.getLoginUserDeptId()));
        // 什么也没有上传,只更新基础信息后返回
        if(null != saveReqVO.getId() && null == saveReqVO.getFile() && StringUtils.isBlank(saveReqVO.getAbsDiskFilePath())){
            MaterialDO oldMaterial = getMaterial(saveReqVO.getId());
            material.setDuration(oldMaterial.getDuration());
            material.setFileSize(oldMaterial.getFileSize());
            material.setContainFileNum(oldMaterial.getContainFileNum());
            material.setFileUploadPath(oldMaterial.getFileUploadPath());
            material.setFileOriginalName(oldMaterial.getFileOriginalName());
            material.setSuplierId(oldMaterial.getSuplierId());
            materialMapper.updateById(material);
            return material.getId();
        }

        if(null != saveReqVO.getFile() && StringUtils.isNotBlank(saveReqVO.getAbsDiskFilePath())){
            throw new BusinessException("不能同时上传文件和设置本地文件路径");
        }
        if(null == saveReqVO.getFile()){
            if(StringUtils.isBlank(saveReqVO.getAbsDiskFilePath())){
                throw new BusinessException("上传文件和本地文件路径不能同时为空");
            }else{
                File f = null;
                try {
                    f = new File(commonUploadPath+saveReqVO.getAbsDiskFilePath());
                } catch (Exception e) {
                    log.error("读取本地文件失败，commonUploadPath={},filePath={}",commonUploadPath,saveReqVO.getAbsDiskFilePath());
                    throw new BusinessException("读取文件失败，请重试设置路径");
                }
                if(f.exists() && f.isFile() &&
                        (f.getAbsolutePath().endsWith("zip") || f.getAbsolutePath().endsWith("ZIP"))){
                    if(f.length() > (1024*1024*1024)){
                        throw new BusinessException("文件太大，请上传不大于1G的文件");
                    }
                    material.setFileSize(f.length());
                    material.setFileOriginalName(f.getName());
                }else{
                    throw new BusinessException("上传文件不存在或者不是压缩包");
                }
            }
        }else{
            if(saveReqVO.getFile().getSize() > (1024*1024*1024)){
                throw new BusinessException("文件太大，请上传不大于1G的文件");
            }
            material.setFileSize(saveReqVO.getFile().getSize());
            material.setFileOriginalName(saveReqVO.getFile().getOriginalFilename());
        }

        // 保存zip文件
        String[] savePathArr ;
        try {
            savePathArr = dataSetUtil.saveZipFile(saveReqVO.getFile(), new File(commonUploadPath+saveReqVO.getAbsDiskFilePath()));
            material.setFileUploadPath(savePathArr[0]);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        // 解压zip文件
        List<MaterialFileVo> releaseFileList = null;
        try {
            releaseFileList = dataSetUtil.fileUncompressing(savePathArr[1], savePathArr[3]);
        } catch (IOException e) {
            log.error("解压文件出现异常，保存失败",e);
            throw new BusinessException("解压文件出现异常，保存失败");
        }
        Long totalDuration = 0L;
        List<MaterialReleaseFileDO> targetList = new ArrayList<>();
        if(CollectionUtils.isNotEmpty(releaseFileList)){
            for (int i = 0; i < releaseFileList.size(); i++) {
                MaterialReleaseFileDO releaseFileDO = new MaterialReleaseFileDO();
                releaseFileDO.setFileType(saveReqVO.getFileType());
                releaseFileDO.setFileOriginalName(releaseFileList.get(i).getOriginalFileName());
                File saveFile = releaseFileList.get(i).getFile();
                releaseFileDO.setFileSize(saveFile.length());
                int fileIndex = i+1 ;
                releaseFileDO.setSortNum(fileIndex);
                releaseFileDO.setFileUploadPath(savePathArr[2]+releaseFileList.get(i).getFileName());
                try {
                    long ls = dataSetUtil.getMp4Seconds(saveFile.getAbsolutePath());
                    totalDuration = totalDuration + ls;
                    releaseFileDO.setDuration(ls);
                } catch (Exception e) {
                    log.warn("获取时长异常:"+saveFile.getAbsoluteFile());
                    //throw new RuntimeException(e);
                    releaseFileDO.setDuration(0L);
                }
                targetList.add(releaseFileDO);
            }
            material.setContainFileNum(targetList.size());
        }else{
            throw new BusinessException("未能从压缩包中解析到文件，操作失败");
        }
        material.setDuration(totalDuration);
        // 是修改
        if(null != saveReqVO.getId()){
            // 先删除关联的记录
            materialReleaseFileService.deleteByMaterialId(saveReqVO.getId());
            materialMapper.updateById(material);
        }else{
            // 新增
            materialMapper.insert(material);
        }

        // 重新保存解压文件信息
        if(CollectionUtils.isNotEmpty(targetList)){
            for (MaterialReleaseFileDO materialReleaseFileDO : targetList) {
                materialReleaseFileDO.setMaterialId(material.getId());
            }
            materialReleaseFileMapper.insertBatch(targetList);
        }
        return material.getId();
    }

    @Override
    public void updateMaterial(MaterialSaveReqVO updateReqVO) {
        // 校验存在
        validateMaterialExists(updateReqVO.getId());
        // 更新
        MaterialDO updateObj = BeanUtils.toBean(updateReqVO, MaterialDO.class);
        materialMapper.updateById(updateObj);
    }

    @Override
    public void deleteMaterial(Long id) {
        // 校验存在
        validateMaterialExists(id);
        // 删除
        materialMapper.deleteById(id);
    }

    private void validateMaterialExists(Long id) {
        if (materialMapper.selectById(id) == null) {
            throw exception(MATERIAL_NOT_EXISTS);
        }
    }

    @Override
    public MaterialDO getMaterial(Long id) {
        return materialMapper.selectById(id);
    }

    @Override
    public PageResult<MaterialListVO> getMaterialPage(MaterialPageReqVO pageReqVO) {
        PageResult<MaterialDO> materialDOPageResult = materialMapper.selectPage(pageReqVO);
        if(CollectionUtils.isEmpty(materialDOPageResult.getList())){
            return PageResult.empty();
        }
        List<MaterialListVO> materialListVOS = BeanUtils.toBean(materialDOPageResult.getList(), MaterialListVO.class);
        handleOtherData(materialListVOS);
        return new PageResult<>(materialListVOS, materialDOPageResult.getTotal());
    }

    @Override
    public MaterialRespVO getMaterialAndFileList(Long id) {
        MaterialDO material = getMaterial(id);
        if(null != material){
            MaterialRespVO respVO = BeanUtils.toBean(material, MaterialRespVO.class);
            handleOtherData(respVO);
            return respVO;
        }
        return null;
    }

    /**
     * 处理其他信息
     * @param materialListVOS
     */
    private void handleOtherData(List<MaterialListVO> materialListVOS){
        // 设置首页显示的图片
        if(CollectionUtils.isNotEmpty(materialListVOS)){
            // 设置创建者和上传者
            List<Long> creatorList = materialListVOS.stream().filter(t -> StringUtils.isNotBlank(t.getCreator()))
                    .map(t -> Long.parseLong(t.getCreator())).collect(Collectors.toList());
            Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(creatorList);
            materialListVOS.forEach(materialListVO -> {
                MapUtils.findAndThen(userMap, Long.parseLong(materialListVO.getCreator()),
                        user -> materialListVO.setCreatorName(user.getNickname()));
                MapUtils.findAndThen(userMap, Long.parseLong(materialListVO.getCreator()),
                        user -> materialListVO.setUploaderName(user.getNickname()));
            });
            //
            List<Long> idList = materialListVOS.stream().map(MaterialListVO::getId).collect(Collectors.toList());
            QueryWrapper<MaterialReleaseFileDO> objectQueryWrapper = new QueryWrapper<>();
            objectQueryWrapper.in("material_id",idList);
            objectQueryWrapper.eq("sort_num",1);
            List<MaterialReleaseFileDO> files = materialReleaseFileMapper.selectList(objectQueryWrapper);
            if(CollectionUtils.isNotEmpty(files)){
                Map<Long, MaterialReleaseFileDO> materialReleaseFileDOMap = files.stream().collect(Collectors.toMap(MaterialReleaseFileDO::getMaterialId, Function.identity(), (v1, v2) -> v1));
                for (MaterialListVO materialListVO : materialListVOS) {
                    MaterialReleaseFileDO materialReleaseFileDO = materialReleaseFileDOMap.get(materialListVO.getId());
                    if(null != materialReleaseFileDO){
                        materialListVO.setFirstFileUrl(materialReleaseFileDO.getFileUploadPath());
                    }
                    // 设置业务字典
                    materialListVO.setFileTypeName(DictFrameworkUtils.getDictDataLabel(DicConstant.materialType, materialListVO.getFileType()));
                    materialListVO.setSenceTypeName(DictFrameworkUtils.getDictDataLabel(DicConstant.sceneClassification, materialListVO.getSenceType()));
                    materialListVO.setSourceTypeName(DictFrameworkUtils.getDictDataLabel(DicConstant.materialLibraryType, materialListVO.getSourceType()));
                }
            }
        }
    }

    /**
     * 处理其他信息
     * @param materialRespVO
     */
    private void handleOtherData(MaterialRespVO materialRespVO){
        // 设置首页显示的图片
        if(null != materialRespVO){
            // 设置创建者和上传者名称
            if(null != materialRespVO.getCreator()){
                AdminUserRespDTO user = adminUserApi.getUser(Long.parseLong(materialRespVO.getCreator()));
                if(null != user){
                    materialRespVO.setCreatorName(user.getNickname());
                    materialRespVO.setUploderName(user.getNickname());
                }
            }

            QueryWrapper<MaterialReleaseFileDO> objectQueryWrapper = new QueryWrapper<>();
            objectQueryWrapper.eq("material_id",materialRespVO.getId());
            List<MaterialReleaseFileDO> files = materialReleaseFileMapper.selectList(objectQueryWrapper);
            if(CollectionUtils.isNotEmpty(files)){
                List<MaterialReleaseFileRespVO> fileVos = BeanUtils.toBean(files, MaterialReleaseFileRespVO.class);
                materialRespVO.setReleaseFileList(fileVos);
                materialRespVO.setFirstFileUrl(fileVos.get(0).getFileUploadPath());
            }
            // 设置业务字典
            materialRespVO.setFileTypeName(DictFrameworkUtils.getDictDataLabel(DicConstant.materialType, materialRespVO.getFileType()));
            materialRespVO.setSenceTypeName(DictFrameworkUtils.getDictDataLabel(DicConstant.sceneClassification, materialRespVO.getSenceType()));
            materialRespVO.setSourceTypeName(DictFrameworkUtils.getDictDataLabel(DicConstant.materialLibraryType, materialRespVO.getSourceType()));
        }
    }

}